#ifndef PARAMREADER_H
#define PARAMREADER_H

// This module reads a parameter file and creates a linked list
// of parameters. Parameters are accessable through mutator functions
// which abstract the underlying storage mechanism.

// The format for a parameter is parameter_name = parameter_value.
// Anything following a '#' or '//' is interpreted as a comment.
// A non empty line without an '=' is considered invalid input.
// Empty lines and spaces are deleted.

// This module does not do any sort of validation and does not know
// anything about the meaning of the parmeters it reads in.

// Also, all parameters are currently stored as strings.
// This class really just does the work of reading in parameters and
// values. Another class should do the work of interpreting them before
// use.

// This class does contain functionality to convert parameter values to
// different types, though it does nothing to check whether doing so
// makes sense.

#include <iostream>
#include <fstream>
#include <string>
#include <sstream>
#include <exception>

using namespace std;

// Define expections for when users try to do things they can't do.

// Sought parameter does not exist.
class ParameterNotFoundException:public exception
{
   virtual const char* what() const throw()
   {
      return "Attempted to access a parameter which does not exist.";
   }
};

// Type conversion does not make sense or is ambigous.
class InvalidTypeConversionException:public exception
{
   virtual const char* what() const throw()
   {
      return "Attempting to perform a type conversion which is either illegal or ambigous.";
   }
};


class ParamReader {
   public:
      // Default constructor. Makes a parameter holder with no parameters.
      ParamReader() {
         paramList = NULL;
         }
      
      // Constructor which reads in a parameter file.
      ParamReader(string fn){
	 paramList = NULL;
         readParamFile(fn);
	 }

      // Open a file and read in the parameters.
      int readParamFile(string fn)
      {
         ifstream in;
	 in.open(fn.c_str());

	 string line;

	 while (in.good()) {
	    getline(in, line);
            line = prepLine(line);
	    if (line.length() > 0)
	       addToList(parseParameter(line), parseValue(line));
	    }

	 printParameters();
      }

      // Accessors and mutators for parameters.
      bool hasParameter(string par) {
         paramNode* tip = paramList;

	 while (tip != NULL) {
	    if (tip->param.compare(par) == 0)
	       return true;

	    tip = tip -> next;
	    }

	 return false;
      }

      // Returns the value of the parameter as a string.
      // If the parameter is not found, throws an error.
      string getParameter(string par) {
	 paramNode* tip = paramList;

	 while (tip != NULL) {
	    if (tip -> param.compare(par) == 0)
	       return tip -> value;
	    tip = tip -> next;

	    }

	 throw(ParameterNotFoundException());
      }

      // If the specified parameter already exists and has a value,
      // overwrite the current value.
      // If it does not already exist, add it to the list.
      void setParameter(string par, string val)
      {
         paramNode* tip = paramList;

	 while (tip != NULL && tip -> param.compare(par) != 0)
	    tip = tip -> next;

	 if (tip != NULL)
	    tip -> value = val;

	 else
	    addToList(par, val);
      }

      // Write the current values of all parameters to a specified file.
      void writeParametersToFile(string fn)
      {
         ofstream out;
	 out.open(fn.c_str());

	 paramNode* tip = paramList;

	 while (tip != NULL) {
	    out << tip -> param << "=" << tip -> value << endl;
	    tip = tip -> next;
	    }

	 out.close();
      }

      // Convert the value of the specified parameter to a different
      // type of data.

      // Return the parameter's value as an int.
      int getParameterI(string par)
      {
         string val = getParameter(par);
         stringstream ss(val);

	 int i;
	 ss >> i;

	 return i;
      }

      // Return the parameter's value as a boolean.
      bool getParameterB(string par)
      {
         string val = getParameter(par);

	 if (val.compare("True") == 0)
	    return true;

	 if (val.compare("False") == 0)
	    return false;

	 if (val.compare("true") == 0)
	    return true;

	 if (val.compare("false") == 0)
	    return false;

	 if (val.compare("0") == 0)
	    return true;

	 else
	    return false;
      }

      // Return the parameter's value as a char.
      // Throw an error if the value is more than one character long.
      char getParameterC(string par)
      {
         string val = getParameter(par);

	 if (val.length() != 1)
	    throw InvalidTypeConversionException();

	 return val[0];
      }

      // Return the parameter's value as a float.
      float getParameterF(string par) {
         string val = getParameter(par);
         stringstream ss(val);

	 float i;
	 ss >> i;

	 return i;
      }

      // Return the parameter's value as a double.
      double getParameterD(string par) {
         string val = getParameter(par);
         stringstream ss(val);

	 double i;
	 ss >> i;

	 return i;
      }

      // Returns the value of the parameter as a string.
      string getParameterS(string par)
      {
         return getParameter(par);
      }

   private:
      class paramNode {
         public:
            string param;
	    string value;
	    paramNode* next;
      };

      paramNode* paramList;

      // Add a parameter-value pair to the list.
      void addToList(string par, string val)
      {
         paramNode* newP = new paramNode();
	 newP -> param = par;
	 newP -> value = val;
	 newP -> next = NULL;

	 // Inserting into an empty list.
         if (paramList == NULL)
	    paramList = newP;

         // Non empty list.
	 else {
	    newP -> next = paramList;
	    paramList = newP;
	    }
      }
      
      // Removes comments and spaces
      string prepLine(string line){
         // Remove comments.
	 int pos = line.find("#");

	 if (pos != string::npos)
	    line = line.substr(0, pos);

	 pos = line.find("//");

	 if (pos != string::npos)
	    line = line.substr(0, pos);

	 // Remove spaces
	 for (int i = 0; i < line.length(); i++) {
	    if (line[i] == ' ') {
	       line = line.substr(0, i) + line.substr(i+1, line.length());
	       //i++;
	       }
	    }

	 return line;
      }

      // Figure out what part of the string is the parameter.
      // Requires a string of non zero length with no spaces
      // or comments. Must have an equal sign.
      string parseParameter(string s) {
         int pos = s.find('=');
	 return s.substr(0 ,pos);
      }

      // Figure out what part of the string is the value.
      // Requires a string of non zero length with no spaces
      // or comments. Must have an equal sign.
      string parseValue(string s) {
         int pos = s.find('=');
	 return s.substr(pos+1, s.length());
      }

      // Print out the pairs of parameters. Only for debugging.
      void printParameters()
      {
         paramNode* tip = paramList;

	 while (tip!= NULL) {
	    cout << "'" << tip -> param << "' '" << tip -> value << "'" << endl;
	    tip = tip -> next;
	    }
      }

};

#endif //PARAMREADER_H
